package lambda.jdkfunctioninterface;

import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.stream.Stream;
import com.google.common.collect.Lists;
import org.testng.annotations.Test;

/**
 * @author topjoy
 */
public class MyFunction {

    public static void main(String[] args) {

        executeOrder();

        chainAdd();

        combineFunctions();

        consumerWithIndex();

    }


    static void executeOrder() {
        Function<Integer, Integer> times2 = e -> e * 2;
        Function<Integer, Integer> squared = e -> e * e;

        System.out.println(times2.apply(4));
        System.out.println(squared.apply(4));

        /* 先执行参数：4 * 4 = 16，
         * 再执行调用者： 16 * 2 = 32
         */
        System.out.println("result: " + times2.compose(squared).apply(4));
        /*
         * 先执行调用者： 4 * 2 = 8
         * 再执行传入andThen的function 8 * 8 = 64
         */
        System.out.println("result: " + times2.andThen(squared).apply(4));
    }

    /**
     * 连续调用apply方法
     */
    public static void chainAdd() {
        Function<Integer, Function<Integer, IntFunction<Integer>>> addfun3 = x -> y -> z -> x + y + z;
        Function<Integer, Function<Integer, IntFunction<Integer>>> addAndSubtraction = x -> y -> z -> x - y + z;


        // apply的调用次数需要和定义的Function对象数量一致
        //        Integer sum2 = addfun3.apply(1);
        //        Integer sum3 = addfun3.apply(1).apply(2);

        Integer sum = addfun3.apply(1).apply(2).apply(3);
        Integer sum2 = addAndSubtraction.apply(1).apply(2).apply(3);

        System.out.println("addfun3 :" + sum);
        System.out.println("addAndSubtraction :" + sum2);
    }

    @SafeVarargs
    private static <R> Function<R, R> combineFunctions(Function<R, R>... functions) {
        return Arrays.stream(functions)
            // 先执行调用者，再执行传入andThen的function
            .reduce(Function::andThen)
            .orElseThrow(() -> new IllegalArgumentException("No functions to combine"));

    }

    static void combineFunctions() {
        Function<Integer, Integer> mulFun2 = x -> x * x;
        final Integer apply = combineFunctions(mulFun2, mulFun2).apply(2);
        System.out.println(apply);

        String str = "1,2,3,4,5,6";
        Function<Object, Object> splitByComma = s -> ((String) s).split(",");
        Function<Object, Object> convertToInt = tokens -> Stream.of((String[]) tokens).map(Integer::valueOf).toArray(Integer[]::new);
        Function<Object, Object> findMax = ints -> Stream.of((Integer[]) ints).max(Integer::compare).get();
        Integer max = (Integer) combineFunctions(splitByComma, convertToInt, findMax).apply(str);
        System.out.println(max);
    }


    static void test4(){
        Function<Integer, Integer> addFun2 = x -> x * x;
        final Calculator<Integer, Integer> calculator = new Calculator<>(2);
        final Integer integer = calculator.combineFunctions(addFun2, addFun2);
        System.out.println(integer);
    }

    static <T> Consumer<T> consumerWithIndex(BiConsumer<T, Integer> consumer) {
        class Obj {
            int i;
        }
        // 只会被调用一次，原因看java.lang.Iterable#forEach
        Obj obj = new Obj();

        // 返回的Consumer函数
        return t -> {
            int index = obj.i++;

            consumer.accept(t, index);
        };
    }

    @Test
    public static void consumerWithIndex() {
        List<String> list = Arrays.asList("Hi", "I", "am", "Henry.Yao");

        list.forEach(LambdaUtils.consumerWithIndex((item, index) -> {
            System.out.println("list[" + index + "]=" + item);
        }));

        // 2个元素为一组
        List<List<String>> partition = Lists.partition(list, 2);
        partition.forEach(LambdaUtils.consumerWithIndex((item, index) -> {
            System.out.println("list[" + index + "]=" + item);
        }));
    }
}

/**
 * 领域开发模式或多或少都听过、了解过，对象除了有自身的属性之外还可以有自身的行为，
 * 针对对象的行为方法也是可以使用函数式编程范式来定义的
 * @param <R>
 * @param <T>
 */
class Calculator<R,T> {

    /**
     * 被操作的属性
     */
    private Object input;

    public Calculator(Object input) {
        this.input = input;
    }

    /**
     * 可以通过这种方式定义对象自身的行为
     * @param functions
     * @return
     */
    @SuppressWarnings("unchecked")
    @SafeVarargs
    public final R combineFunctions(Function<T, T>... functions) {
        return (R) Arrays.stream(functions)
            .reduce(Function::andThen)
            .orElseThrow(() -> new IllegalArgumentException("No functions to combine"))
            .apply((T)input);
    }
}

class LambdaUtils {
    /**
     *
     * @param consumer
     * @param <T>
     * @return
     */
    public static <T> Consumer<T> consumerWithIndex(BiConsumer<T, Integer> consumer) {
        class Obj {
            int i;
        }
        Obj obj = new Obj();
        return t -> {
            int index = obj.i++;
            consumer.accept(t, index);
        };
    }
}

